﻿using CoachingOrganizer.ClientInfrastructure.ServicesEntities.LocalStorage.Tools.MoneyCalculation;
using CoachingOrganizer.Logic.Common;
using CoachingOrganizer.Logic.Models.Tools.MoneyCalculation;
using CoachingOrganizer.Toolkit.Events;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace CoachingOrganizer.Logic.Logic.Tools.MoneyCalculation
{
    internal class MoneyFlowLogic
    {
        private readonly MoneyFlowModel _model;
        private readonly LogicExtender _logicExtender;
        private readonly MoneyCalculationLogic _upperLogic;
        private readonly List<StableMoneyFlowItemLogic> _stableItems = new List<StableMoneyFlowItemLogic>();
        private readonly List<OccasionalMoneyFlowItemLogic> _occasionalItems = new List<OccasionalMoneyFlowItemLogic>();
        
        public MoneyFlowModel Model
        {
            get { return _model; }
        } 

        internal List<StableMoneyFlowItemLogic> StableItems
        {
            get { return _stableItems; }
        }

        internal List<OccasionalMoneyFlowItemLogic> OccasionalItems
        {
            get { return _occasionalItems; }
        }

        public MoneyFlowLogic(MoneyFlowModel model, LogicExtender logicExtender, MoneyCalculationLogic upperLogic)
        {
            _model = model;
            _logicExtender = logicExtender;
            _upperLogic = upperLogic;
            model.EnumerateStableMoneyFlowItemsDelegate = EnumerateStableMoneyFlowItems;
            model.EnumerateOccasionalMoneyFlowItemsDelegate = EnumerateOccasionalMoneyFlowItems;
            model.AddNewStableMoneyFlowItemDelegate = AddNewStableMoneyFlowItem;
            model.AddNewOccasionalMoneyFlowItemDelegate = AddNewOccasionalMoneyFlowItem;
            model.EnumerateStableItemsNameSuggestionsDelegate = EnumerateStableItemsNameSuggestions;
            model.EnumerateOccasionalItemsNameSuggestionsDelegate = EnumerateOccasionalItemsNameSuggestions;
        }

        public decimal GetValueForPeriod(DateTime start, DateTime end, bool useSign)
        {
            decimal absValue = _stableItems.Sum(si => si.GetPeriodAmount(start, end)) + _occasionalItems.Where(oi => oi.GetIsInPeriod(start, end)).Sum(oi => oi.Model.Amount);
            return useSign ? _model.Sign * absValue : absValue;
        }

        public IEnumerable<MoneyFlowItemNameSuggestionLogic> EnumerateStableItemsNameSuggestions()
        {
            return _stableItems.GroupBy(i => i.Model.Name).Select(ig => new MoneyFlowItemNameSuggestionLogic(_logicExtender, this, suggestedName: ig.Key, forStableItems: true)
            {
                Frequency = ig.Count()
            }).OrderByDescending(s => s.Frequency);
        }

        public IEnumerable<MoneyFlowItemNameSuggestionLogic> EnumerateOccasionalItemsNameSuggestions()
        {
            return _occasionalItems.GroupBy(i => i.Model.Name).Select(ig => new MoneyFlowItemNameSuggestionLogic(_logicExtender, this, suggestedName: ig.Key, forStableItems: false)
            {
                Frequency = ig.Count()
            }).OrderByDescending(s => s.Frequency);
        }

        public ActionResult RemoveStableItem(StableMoneyFlowItemLogic logicObject)
        {
            ActionResult outResult = logicObject.LocalData.Remove();
            if (outResult.IsValid)
                _stableItems.Remove(logicObject);
            return outResult;
        }

        public ActionResult RemoveOccasionalItem(OccasionalMoneyFlowItemLogic logicObject)
        {
            ActionResult outResult = logicObject.LocalData.Remove();
            if (outResult.IsValid)
                _occasionalItems.Remove(logicObject);
            return outResult;
        }

        private ActionResult<IEnumerable<StableMoneyFlowItemLogic>> EnumerateStableMoneyFlowItems()
        {
            ActionResult loadResult = _upperLogic.LocalData.LoadStableItems();
            IEnumerable<IStableMoneyFlowItemInfo> stableItemsInfo = _upperLogic.LocalData.StableItemsInfo;
            return loadResult.IsValid ? ActionResult<IEnumerable<StableMoneyFlowItemLogic>>.GetValidResult(stableItemsInfo.Select(i => CreateStableMoneyFlowItemLogic(i))) : ActionResult<IEnumerable<StableMoneyFlowItemLogic>>.GetErrorResult(loadResult);
        }

        private ActionResult<IEnumerable<OccasionalMoneyFlowItemLogic>> EnumerateOccasionalMoneyFlowItems()
        {
            ActionResult loadResult = _upperLogic.LocalData.LoadOccasionalItems();
            IEnumerable<IOccasionalMoneyFlowItemInfo> occasionalItemsInfo = _upperLogic.LocalData.OccasionalItemsInfo;
            return loadResult.IsValid ? ActionResult<IEnumerable<OccasionalMoneyFlowItemLogic>>.GetValidResult(occasionalItemsInfo.Select(i => CreateOccasionalMoneyFlowItemLogic(i))) : ActionResult<IEnumerable<OccasionalMoneyFlowItemLogic>>.GetErrorResult(loadResult);
        }

        private StableMoneyFlowItemLogic CreateStableMoneyFlowItemLogic(IStableMoneyFlowItemInfo info)
        {
            StableMoneyFlowItemLogic outResult = new StableMoneyFlowItemLogic(new StableMoneyFlowItemModel
            {
                Start = info.Start,
                Finish = info.Finish,
                Period = info.Period,
                Amount = info.Amount,
                Name = info.Name
            }, _logicExtender, this, info);
            _stableItems.Add(outResult);
            return outResult;
        }

        private OccasionalMoneyFlowItemLogic CreateOccasionalMoneyFlowItemLogic(IOccasionalMoneyFlowItemInfo info)
        {
            OccasionalMoneyFlowItemLogic outResult = new OccasionalMoneyFlowItemLogic(new OccasionalMoneyFlowItemModel
            {
                Time = info.Time,
                Amount = info.Amount,
                Name = info.Name
            }, _logicExtender, this, info);
            _occasionalItems.Add(outResult);
            return outResult;
        }

        private ActionResult<StableMoneyFlowItemLogic> AddNewStableMoneyFlowItem(Action<IStableMoneyFlowItemInfo> initCallback)
        {
            ActionResult<IStableMoneyFlowItemInfo> addResult = _upperLogic.LocalData.AddNewStableItem(initCallback);
            return addResult.IsValid ? ActionResult<StableMoneyFlowItemLogic>.GetValidResult(CreateStableMoneyFlowItemLogic(addResult.Result)) : ActionResult<StableMoneyFlowItemLogic>.GetErrorResult(addResult);
        }

        private ActionResult<OccasionalMoneyFlowItemLogic> AddNewOccasionalMoneyFlowItem(Action<IOccasionalMoneyFlowItemInfo> initCallback)
        {
            ActionResult<IOccasionalMoneyFlowItemInfo> addResult = _upperLogic.LocalData.AddNewOccasionalItem(initCallback);
            return addResult.IsValid ? ActionResult<OccasionalMoneyFlowItemLogic>.GetValidResult(CreateOccasionalMoneyFlowItemLogic(addResult.Result)) : ActionResult<OccasionalMoneyFlowItemLogic>.GetErrorResult(addResult);
        }
    }
}
